#include <string.h>
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>

#include <psp2/ctrl.h>

#include "input.h"
#include "utils.h"
#include "menu.h"
#include "config.h"
#include "audio.h"
#include "retro.h"
#include "video.h"
#include "init.h"

#include "libretro.h"

#define RETRO_DEVICE_ID_JOYPAD_N_BUTTONS 16

static uint32_t key_map_list[4];

extern int retro_input_bitmasks;
extern int retro_input_polled;

void setKeyMapping(int press, int port, int pad_id, int value)
{
    if (press || current_pads[port][pad_id])
    {
        current_pads[port][pad_id] = 1;
        if (value & ENABLE_BUTTON_MASK)
        {
            if (value & TURBO_BUTTON_MASK)
            {
                hold_counts[port][pad_id]++;
                if (!old_pads[port][pad_id] || (hold_counts[port][pad_id] > control_config.turbo_delay))
                {
                    key_map_list[port] |= (value & 0x00FFFFFF);
                    hold_counts[port][pad_id] = 0;
                }
            }
            else
            {
                key_map_list[port] |= (value & 0x00FFFFFF);
            }
        }
    }
    else
    {
        current_pads[port][pad_id] = 0;
        hold_counts[port][pad_id] = 0;
    }
}

void retroInputPollCallback()
{
    retro_input_polled = 1;
    return;
}

void retroInputPoll()
{
    int port, i, j;

    for (i = 0; i < N_PADS; i++)
    {
        memcpy(old_pads[i], current_pads[i], sizeof(Pad));
        memset(current_pads[i], 0, sizeof(Pad));
        memset(&ctrl_data_entries[i], 0, sizeof(CtrlDataEntry));
        key_map_list[i] = 0;

        if (i > 0)
        {
            port = i + 1;
        }
        else
        {
            port = 0;
            readTouch();
        }
        
        int ret = sceCtrlPeekBufferPositiveExt2(port, &ctrl_data_entries[i].pad, 1);
        if (ret < 0)
        {
            ctrl_data_entries[i].error = 1;
            continue;
        }

        // if (CHECK_EXIT_APP(ctrl_data_entries[i].pad.buttons))
        // exitApp();

        ControlOptionValue *option;
        uint32_t value;
        uint32_t local_key;
        int press;
        for (j = 0; j < N_LOCAL_BUTTONS; j++)
        {
            option = control_entries[j].option;
            value = *(option->value);
            local_key = option->local_key;
            press = ctrl_data_entries[i].pad.buttons & local_key;
            setKeyMapping(press, i, j, value);
        }

        if (control_config.left_analog == 1)
        {
            press = ctrl_data_entries[i].pad.lx < ANALOG_CENTER - ANALOG_THRESHOLD;
            setKeyMapping(press, i, PAD_LEFT_ANALOG_LEFT, control_config.button_left);

            press = ctrl_data_entries[i].pad.lx > ANALOG_CENTER + ANALOG_THRESHOLD;
            setKeyMapping(press, i, PAD_LEFT_ANALOG_RIGHT, control_config.button_right);

            press = ctrl_data_entries[i].pad.ly < ANALOG_CENTER - ANALOG_THRESHOLD;
            setKeyMapping(press, i, PAD_LEFT_ANALOG_UP, control_config.button_up);

            press = ctrl_data_entries[i].pad.ly > ANALOG_CENTER + ANALOG_THRESHOLD;
            setKeyMapping(press, i, PAD_LEFT_ANALOG_DOWN, control_config.button_down);
        }

        if (control_config.right_analog == 1)
        {
            press = ctrl_data_entries[i].pad.rx < ANALOG_CENTER - ANALOG_THRESHOLD;
            setKeyMapping(press, i, PAD_RIGHT_ANALOG_LEFT, control_config.button_left);

            press = ctrl_data_entries[i].pad.rx > ANALOG_CENTER + ANALOG_THRESHOLD;
            setKeyMapping(press, i, PAD_RIGHT_ANALOG_RIGHT, control_config.button_right);

            press = ctrl_data_entries[i].pad.ry < ANALOG_CENTER - ANALOG_THRESHOLD;
            setKeyMapping(press, i, PAD_RIGHT_ANALOG_UP, control_config.button_up);

            press = ctrl_data_entries[i].pad.ry > ANALOG_CENTER + ANALOG_THRESHOLD;
            setKeyMapping(press, i, PAD_RIGHT_ANALOG_DOWN, control_config.button_down);
        }

        if (ctrl_data_entries[i].pad.buttons & SCE_CTRL_PSBUTTON)
        {
            current_pads[i][PAD_PSBUTTON] = 1;
            hold_counts[i][PAD_PSBUTTON]++;
            if (hold_counts[i][PAD_PSBUTTON] >= SKIP_PRESSED_PSBUTTON_COUNT)
                skipPressedPsbutton();

            key_map_list[i] = 0;
            if (!current_pads[i][PAD_CROSS] && old_pads[i][PAD_CROSS])
            {
                skipPressedPsbutton();
                exitGameInRunning();
            }
            else if (!current_pads[i][PAD_SQUARE] && old_pads[i][PAD_SQUARE])
            {
                skipPressedPsbutton();
                saveStateInRunning();
            }
            else if (!current_pads[i][PAD_TRIANGLE] && old_pads[i][PAD_TRIANGLE])
            {
                skipPressedPsbutton();
                loadStateInRunning();
            }
            else if (!current_pads[i][PAD_R1] && old_pads[i][PAD_R1])
            {
                skipPressedPsbutton();
                float fps_scale = vitaVideoGetFpsScale();
                fps_scale += 0.5f;
                if (fps_scale > 2.0f)
                    fps_scale = 1.0f;
                vitaVideoSetFpsScale(fps_scale);
            }
            else if (!current_pads[i][PAD_L1] && old_pads[i][PAD_L1])
            {
                skipPressedPsbutton();
                float fps_scale = vitaVideoGetFpsScale();
                fps_scale -= 0.5f;
                if (fps_scale < 1.0f)
                    fps_scale = 2.0f;
                vitaVideoSetFpsScale(fps_scale);
            }
            return;
        }
        else
        {
            hold_counts[i][PAD_PSBUTTON] = 0;

            if (!old_pads[i][PAD_PSBUTTON])
            {
                unskipPressedPsbutton();
            }
            else if (!isSkipPressedPsbutton())
            {
                float fps_scale = vitaVideoGetFpsScale();
                if (fps_scale > 1.0f)
                {
                    fps_scale = 1.0f;
                    vitaVideoSetFpsScale(fps_scale);
                }
                else
                {
                    key_map_list[i] = 0;
                    pauseGame();
                    enterMenu();
                    return;
                }
            }
        }
    }
}

int16_t retroInputStateCallback(unsigned port, unsigned device, unsigned index, unsigned id)
{
    if (port >= N_PADS || ctrl_data_entries[port].error)
        return 0;

    int16_t ret = 0;
    
    if (device == RETRO_DEVICE_JOYPAD)
    {
        if (retro_input_bitmasks)
            ret = key_map_list[port] & 0xFFFF;
        else
            ret = key_map_list[port] & RETRO_KEY_BITMASK(id);
    }

    return ret;
}
